#include "preHeader.h"
#include "SpecialShopMgr.h"
#include "Session/MapServerMgr.h"
#include "InstCfgMgr.h"
#include "GameServer.h"

SpecialShopMgr::SpecialShopMgr()
{
	m_specialShopStatus.reserve(128);
	m_specialShopPrototypes.reserve(128);
}

SpecialShopMgr::~SpecialShopMgr()
{
}

void SpecialShopMgr::LoadData()
{
	LoadSpecialShopStatus();
}

GErrorCode SpecialShopMgr::HandleBuySpecialShopItem(Coroutine::YieldContext& ctx,
	Character* pChar, uint64 itemUniqueKey, uint32 num)
{
	if (num == 0 || num > BUY_SHOP_ITEM_COUNT) {
		return InvalidRequest;
	}
	auto itr = m_specialShopPrototypes.find(itemUniqueKey);
	if (itr == m_specialShopPrototypes.end()) {
		return InvalidRequest;
	}
	auto pProto = &itr->second;
	if (!IsInRange<s64>(GET_UNIX_TIME, pProto->itemBeginTime, pProto->itemEndTime)) {
		return ErrShopOutOfTime;
	}

	auto maxBuyNum = pProto->bundleNumMax != 0 ?
		std::min(pProto->bundleNumMax, num) : num;
	auto GetBuySpecialShopItemMaxCount = [=, &maxBuyNum]() -> GErrorCode {
		auto itr = m_specialShopStatus.find(itemUniqueKey);
		auto& siStatus = itr != m_specialShopStatus.end() ?
			itr->second : defaultShopItemStatus;
		maxBuyNum = std::min(maxBuyNum,
			GetBuySpecialShopItemMaxCount4ServerBuyLimit(pProto, siStatus));
		if (maxBuyNum == 0) {
			return ErrShopItemSellOut;
		}
		return CommonSuccess;
	};
	auto GetRPCInvokeResp = [&ctx, &maxBuyNum]() -> GErrorCode {
		auto& resp = ctx.WaitRPCInvokeResp();
		if (resp.err != RPCErrorNone) {
			return CommonInternalError;
		}
		int32 errCode;
		(*resp.pck) >> errCode;
		if (errCode != CommonSuccess) {
			return (GErrorCode)errCode;
		}
		(*resp.pck) >> maxBuyNum;
		if (maxBuyNum == 0) {
			return ErrShopItemSellOut;
		}
		return CommonSuccess;
	};

	bool isBuyLimit = IsSpecialShopPrototypeServerBuyLimit(pProto);
	if (isBuyLimit) {
		auto errCode = GetBuySpecialShopItemMaxCount();
		if (errCode != CommonSuccess) {
			return errCode;
		}
	}

	NetPacket rpcReqPck(G2M_BUY_SPECIAL_SHOP_ITEM);
	rpcReqPck << false << maxBuyNum;
	SaveToINetStream(*pProto, rpcReqPck);
	sMapServerMgr.RPCInvoke2Player(pChar, rpcReqPck,
		ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
	auto errCode = GetRPCInvokeResp();
	if (errCode != CommonSuccess) {
		return errCode;
	}

	if (isBuyLimit) {
		auto errCode = GetBuySpecialShopItemMaxCount();
		if (errCode != CommonSuccess) {
			return errCode;
		}
	}

	auto allocBuyNum = maxBuyNum;
	if (isBuyLimit) {
		ApplyBuySpecialShopItem4ServerBuyLimit(
			pProto, m_specialShopStatus[itemUniqueKey], maxBuyNum);
	}

	size_t totalSize = rpcReqPck.GetTotalSize();
	rpcReqPck.Shrink(0);
	rpcReqPck << true << maxBuyNum;
	rpcReqPck.Erlarge(totalSize);
	sMapServerMgr.RPCInvoke2Player(pChar, rpcReqPck,
		ctx.GetRPCInvokeCb(), &sGameServer, DEF_S2S_RPC_TIMEOUT);
	if ((errCode = GetRPCInvokeResp()) != CommonSuccess) {
		maxBuyNum = 0;
	}

	if (isBuyLimit) {
		if (allocBuyNum > maxBuyNum) {
			RevertBuySpecialShopItem4ServerBuyLimit(pProto,
				m_specialShopStatus[itemUniqueKey], allocBuyNum - maxBuyNum);
		}
		SaveSpecialShopStatus();
	}

	return errCode;
}

void SpecialShopMgr::PackShopItemList(INetPacket& pck, uint32 shopType) const
{
	uint16 n = 0;
	size_t anchor = pck.Placeholder<u16>(0);
	for (auto& pair : m_specialShopPrototypes) {
		auto pProto = &pair.second;
		if (shopType == 0 || shopType == pProto->shopType) {
			SaveToINetStream(*pProto, pck);
			n += 1;
		}
	}
	pck.Put(anchor, n);

	n = 0;
	anchor = pck.Placeholder<u16>(0);
	for (auto&[itemUniqueKey, ssStatus] : m_specialShopStatus) {
		auto itr = m_specialShopPrototypes.find(itemUniqueKey);
		if (itr != m_specialShopPrototypes.end()) {
			auto pProto = &itr->second;
			if (shopType == 0 || shopType == pProto->shopType) {
				pck << itemUniqueKey << ssStatus.dailyCount
					<< ssStatus.weeklyCount << ssStatus.totalCount;
				n += 1;
			}
		}
	}
	pck.Put(anchor, n);
}

void SpecialShopMgr::SaveSpecialShopStatus()
{
	BEGIN_DELAY_SAVE_INST_CFG_VAL
	TextPacker packer;
	for (auto&[itemUniqueKey, ssStatus] : m_specialShopStatus) {
		auto itr = m_specialShopPrototypes.find(itemUniqueKey);
		if (itr != m_specialShopPrototypes.end()) {
			packer << itemUniqueKey << ssStatus.dailyCount
				<< ssStatus.weeklyCount << ssStatus.totalCount;
			packer.PutDelimiter(';');
		}
	}
	sInstCfgMgr.SaveInstCfgVal(InstCfg_SpecialShopStatus, packer.str());
	END_DELAY_SAVE_INST_CFG_VAL
}

void SpecialShopMgr::LoadSpecialShopStatus()
{
	auto& cfgVal = sInstCfgMgr.GetInstCfgVal(InstCfg_SpecialShopStatus);
	if (cfgVal.empty()) {
		return;
	}
	TextUnpacker unpacker(cfgVal.c_str());
	while (!unpacker.IsEmpty()) {
		uint64 itemUniqueKey;
		ShopItemStatus ssStatus;
		unpacker >> itemUniqueKey >> ssStatus.dailyCount
			>> ssStatus.weeklyCount >> ssStatus.totalCount;
		m_specialShopStatus.emplace(itemUniqueKey, ssStatus);
	}
}
